package com.edu.control;

import com.edu.control.deploy.CommandActuator;
import com.edu.control.deploy.DeployTask;
import com.edu.control.deploy.PodInfo;
import com.edu.control.deploy.constant.Constants;
import com.edu.control.deploy.constant.FaasCli;
import com.edu.control.deploy.sync.DeployStorageRedis;
import com.edu.control.k8s.KubernetesApi;
import com.edu.control.model.DeployRequest;
import com.edu.control.util.OkHttpUtil;
import com.edu.control.util.YamlUtil;
import com.edu.model.Response;
import com.edu.model.constant.FileConfig;
import com.edu.model.constant.ResultMsgConstant;
import com.edu.model.util.OSUtil;
import com.edu.util.ExceptionPointer;
import com.edu.util.file.ZipUtil;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.util.StopWatch;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.concurrent.TimeUnit;

@Service
public class OpenFaasGatewayImpl implements Gateway {

    private static final Logger LOGGER = LoggerFactory.getLogger(OpenFaasGatewayImpl.class);

    @Value("${docker.username:904746285}")
    private String dockerUsername;
    @Value("${docker.password:Young1994++}")
    private String dockerPassword;
    @Value("${docker.password:}")
    private String dockerRepository;

    @Value("${openfaas.username:admin}")
    private String openfaasUsername;
    @Value("${openfaas.password:OBdRp4yV3U7f}")
    private String openfaasPassword;
    @Value("${openfaas.gateway:http://121.43.186.102:31112}")
    private String openfaasGateway;
    @Value("${image.repository:904746285}")
    private String imageRepository;

    @Resource(name = "deployThreadPool")
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    @Resource
    private DeployStorageRedis deployStorageRedis;

    @Resource
    private KubernetesApi kubernetesApi;

    private ClassPathResource openfaasTemplate;

    @PostConstruct
    public void init() throws IOException, InterruptedException {
        openfaasTemplate = new ClassPathResource("/template/openfaas/template");
        OkHttpUtil.setUsername(openfaasUsername);
        OkHttpUtil.setPassword(openfaasPassword);
        FaasCli.LOGIN.setCommand(FaasCli.LOGIN.getCommand() + openfaasUsername + " -p " + openfaasPassword + " -g " + openfaasGateway);
        if (OSUtil.isLinux()) {
            StringBuilder command = new StringBuilder();
            command.append("docker login -u ").append(dockerUsername).append(" -p ").append(dockerPassword).append(" ").append(dockerRepository);
            Process process = Runtime.getRuntime().exec(command.toString(), null, null);
            try {
                process.waitFor(1, TimeUnit.MINUTES);
                LOGGER.info("docker login success, username:{}, password:{}, repository:{}", dockerUsername, dockerPassword, dockerRepository);
            } catch (InterruptedException e) {
                LOGGER.error("docker login timeout 1 minutes.");
                throw e;
            }
        }
    }

    @Override
    public Response deploy(DeployRequest deployRequest) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        try {
            threadPoolTaskExecutor.submit(new DeployTask(deployRequest, this));
        } catch (Exception e) {

        } finally {
            stopWatch.stop();
            LOGGER.info("deploy cost {} ms", stopWatch.getLastTaskTimeMillis());
        }
        return Response.success(ResultMsgConstant.ASYNC_DEPLOY);
    }

    public Response deployFunction(DeployRequest deployRequest) {

        PodInfo podInfo = new PodInfo();
        podInfo.setEventType(deployRequest.getFunctionType());
        podInfo.setExpectNum(deployRequest.getBootConfig().getReplicas());
        podInfo.setFunctionName(deployRequest.getFunctionName());
        podInfo.setLabels(Constants.WATCH_NAME + deployRequest.getFunctionName() + Constants.SPLIT + deployRequest.getDeployVersion());
        podInfo.setVersion(deployRequest.getDeployVersion() + "");
        podInfo.setNamespace("openfaas");
        podInfo.setEventType(deployRequest.getDeployType());

        String filepath = FileConfig.DEFAULT_BUILD_PATH + "function_" + deployRequest.getFunctionName();
        Response before = deployBefore(deployRequest, podInfo, filepath);
        if (before.isFail()) {
            return before;
        }

        try {
            CommandActuator.exec(FaasCli.BUILD, podInfo, filepath, kubernetesApi, deployStorageRedis);
            CommandActuator.exec(FaasCli.PUSH, podInfo, filepath, kubernetesApi, deployStorageRedis);
            CommandActuator.exec(FaasCli.DEPLOY, podInfo, filepath, kubernetesApi, deployStorageRedis, (podInfo1) -> {
                System.out.println("这里需要重写");
            });
        } catch (Exception e) {
        }
        return Response.success();
    }

    /**
     * 1.登录
     * 2.创建当前事件的文件夹
     * 3.拷贝openfaas模板文件
     * 4.下载代码，并解压到openfaas模板到function路径下
     * 5.faas-cli new 初始化部署模板
     * 6.修改部署的yaml文件
     * 7.操作部署文件
     *
     * @param deployRequest
     * @param filepath      当前部署文件的操作路径
     * @return
     */
    private Response deployBefore(DeployRequest deployRequest, PodInfo podInfo, String filepath) {
        try {
            // 登录openfaas，docker的登录在OpenFaasGatewayImpl#init()方法调用了
            loginOpenfaas();
            // 创建文件夹
            String path = newDir(filepath);
            File file = new File(path);
            // 拷贝模板文件
            copyTemplate(deployRequest, file);
            // 下载低代码，并解压到function文件夹下
            downloadAndUnzip2FunctionDir(deployRequest, file);
            // 从模板中新建部署文件
            newDeployFileFromTemplate(filepath, podInfo);
            // 修改模板生成的yaml文件，并覆盖模板文件
            modifyDeployYamlAndOverrideTemplate(deployRequest, file);
            // 最后一次修改部署文件
            redoDeployFile(deployRequest, path);
        } catch (Exception e) {
            ExceptionPointer.echo(e, "deployBefore");
            return Response.fail(e.getMessage());
        }

        return Response.success();
    }

    /**
     * 登录OpenFaaS
     *
     * @throws Exception
     */
    private void loginOpenfaas() throws Exception {
        CommandActuator.exec(FaasCli.LOGIN);
    }

    /**
     * faas new新建部署文件
     *
     * @param filepath
     * @param podInfo
     * @throws Exception
     */
    private void newDeployFileFromTemplate(String filepath, PodInfo podInfo) throws Exception {
        CommandActuator.exec(FaasCli.NEW, podInfo, filepath, kubernetesApi, deployStorageRedis);
    }

    /**
     * 下载代码并解压function文件夹
     *
     * @param deployRequest
     * @param file
     * @throws Exception
     */
    protected void downloadAndUnzip2FunctionDir(DeployRequest deployRequest, File file) throws Exception {
        FileUtils.copyFileToDirectory(new File(deployRequest.getSourceFile()), file);
        FileUtils.copyFileToDirectory(new File(deployRequest.getDatasource()), file);

        File[] zipFiles = file.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                return pathname.getName().endsWith(".zip");
            }
        });

        File fucDir = new File(file, "template/" + deployRequest.getBootConfig().getLang() + "/function/");
        for (File zipFile : zipFiles) {
            ZipUtil.unzip(zipFile, fucDir, Charset.forName("gbk"));
        }
    }

    protected void redoDeployFile(DeployRequest deployRequest, String path) {

    }

    /**
     * 修改yaml文件，并覆盖模板生成的yaml文件
     *
     * @param deployRequest
     * @param file
     * @throws Exception
     * @see template/openfaas/deploy/node12/deploy.yml
     * @see template/openfaas/deploy/springboot/deploy.yml
     */
    protected void modifyDeployYamlAndOverrideTemplate(DeployRequest deployRequest, File file) throws Exception {
        YamlUtil.modifyYaml(deployRequest, imageRepository, openfaasGateway, file);
    }

    /**
     * 拷贝模板文件
     *
     * @param deployRequest
     * @param file
     * @throws Exception
     */
    protected void copyTemplate(DeployRequest deployRequest, File file) throws Exception {
        FileUtils.copyDirectoryToDirectory(openfaasTemplate.getFile(), file);
    }

    private String newDir(String filepath) throws Exception {
        File file = new File(filepath);
        if (file.exists()) {
            FileUtils.deleteDirectory(file);
        }
        file.mkdirs();
        LOGGER.info("functionName's path: {}", file.getCanonicalPath());
        return file.getCanonicalPath();
    }

}
